package com.xmxsuperstar.ant.task;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

import com.xmxsuperstar.ant.task.model.ClassPathEntry;
import com.xmxsuperstar.ant.task.model.LibFileAnalyzier;

public class EclipseProjectTask extends Task {

	private boolean webproject = false;

	private String jvmversion = "1.6";

	private String compileversion = "1.6";

	public void setJvmversion(String jvmversion) {
		this.jvmversion = jvmversion;
	}

	public void setWebproject(boolean webproject) {
		this.webproject = webproject;
	}

	public void setCompileversion(String compileversion) {
		this.compileversion = compileversion;
	}

	public void execute() {
		Pattern p = Pattern.compile("1\\.(\\d)");
		Matcher jm = p.matcher(jvmversion);
		Matcher cm = p.matcher(compileversion);

		int jv, cv;
		if (jm.matches()) {
			jv = Integer.valueOf(jm.group(1));
			if (jv < 4 || jv > 7) {
				throw new BuildException("Invalid jvmversion: " + jvmversion);
			}
		} else {
			throw new BuildException("Invalid jvmversion: " + jvmversion);
		}

		if (cm.matches()) {
			cv = Integer.valueOf(cm.group(1));
			if (cv < 4 || cv > 7) {
				throw new BuildException("Invalid jvmversion: "
						+ compileversion);
			}
		} else {
			throw new BuildException("Invalid jvmversion: " + compileversion);
		}

		if (cv > jv) {
			throw new BuildException(
					"compileversion can not greater than jvmversion");
		}

		createClassDir();
		createClassPath();
		createProject(this.getProject().getName());
		createSettings();

	}

	private void createClassDir() {
		File classFile = null;

		if (this.webproject) {
			classFile = new File(getProject().getBaseDir(),
					"webroot/WEB-INF/classes");
		} else {
			classFile = new File(getProject().getBaseDir(), "bin");
		}

		if (!classFile.exists()) {
			classFile.mkdir();
		}
	}

	private void createClassPath() {
		File baseDirFile = getProject().getBaseDir();
		// File baseDirFile = new File(".");
		File classpathFile = new File(baseDirFile, ".classpath");

		if (classpathFile.exists()) {
			log("Recreating .classpath file");
		}

		String libDir = null;
		String srcDir = null;
		if (webproject) {
			libDir = "webroot/WEB-INF/lib";
			srcDir = "webroot/WEB-INF/src";
		} else {
			libDir = "lib";
			srcDir = "src";
		}

		String javaContainer = "org.eclipse.jdt.launching.JRE_CONTAINER/"
				+ "org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/"
				+ "JavaSE-" + jvmversion;

		List<ClassPathEntry> classPathEntryList = new ArrayList<ClassPathEntry>();

		// Add src dir to classpath
		String[] subSrcDirArray = new String[] { "/main/conf", "/main/java",
				"/test/conf", "/test/java" };
		for (String subSrcDir : subSrcDirArray) {
			classPathEntryList.add(new ClassPathEntry(ClassPathEntry.Type.src,
					srcDir + subSrcDir));
		}

		// Add java container to classpath
		classPathEntryList.add(new ClassPathEntry(ClassPathEntry.Type.con,
				javaContainer));

		// Add libs to classpath
		FileFilter libFileFilter = new FileFilter() {

			@Override
			public boolean accept(File file) {
				if (file.isDirectory()) {
					return false;
				}

				if (!file.getName().endsWith(".jar")
						&& !file.getName().endsWith(".zip")) {
					return false;
				}

				if (file.getName().contains("sources")) {
					return false;
				}

				return true;
			}

		};

		Comparator<File> libFileComparator = new Comparator<File>() {
			@Override
			public int compare(File f1, File f2) {
				return f1.getName().compareTo(f2.getName());
			}
		};

		List<File> allLibFiles = new ArrayList<File>();

		File libDirFile = new File(baseDirFile, libDir);
		File[] osGeneralLibFileArray = libDirFile.listFiles(libFileFilter);

		Arrays.sort(osGeneralLibFileArray, libFileComparator);
		allLibFiles.addAll(Arrays.asList(osGeneralLibFileArray));

		String os = System.getProperty("os.name");
		System.out.println(os);
		File osSpecificLibDirFile = null;
		if (os.toLowerCase().contains("windows")) {
			osSpecificLibDirFile = new File(libDirFile, "win");
		} else if (os.toLowerCase().contains("nix")) {
			osSpecificLibDirFile = new File(libDirFile, "nix");
		}

		if (osSpecificLibDirFile.exists()) {
			File[] osSpecificLibFileArray = osSpecificLibDirFile
					.listFiles(libFileFilter);
			Arrays.sort(osSpecificLibFileArray, libFileComparator);
			allLibFiles.addAll(Arrays.asList(osSpecificLibFileArray));
		}

		for (File libFile : allLibFiles) {
			System.out.println(libFile.getAbsolutePath());
			String libFilePath = baseDirFile.toURI()
					.relativize(libFile.toURI()).toString();

			LibFileAnalyzier lfa = new LibFileAnalyzier(libFile);
			File zipSourceFile = new File(libFile.getParent(), lfa.getLibName()
					+ "-" + lfa.getLibVersion() + "-" + "sources" + ".zip");
			File jarSourceFile = new File(libFile.getParent(), lfa.getLibName()
					+ "-" + lfa.getLibVersion() + "-" + "sources" + ".jar");

			String sourceFilePath = null;
			if (zipSourceFile.exists()) {
				sourceFilePath = baseDirFile.toURI().relativize(
						zipSourceFile.toURI()).toString();
			} else if (jarSourceFile.exists()) {
				sourceFilePath = baseDirFile.toURI().relativize(
						jarSourceFile.toURI()).toString();
			}

			classPathEntryList.add(new ClassPathEntry(ClassPathEntry.Type.lib,
					libFilePath, sourceFilePath));
		}

		// Add bin dir to class path
		classPathEntryList.add(new ClassPathEntry(ClassPathEntry.Type.output,
				webproject ? "webroot/WEB-INF/classes" : "bin"));

		Map<String, Object> paras = new HashMap<String, Object>();
		paras.put("class_path_list", classPathEntryList);
		merge("classpath.vm", classpathFile, paras);
	}

	private void createProject(String projectName) {
		File projectFile = new File(getProject().getBaseDir(), ".project");

		if (projectFile.exists()) {
			log(".project file already exists! Skipped creating .project!");
			return;
		}

		Map<String, Object> paras = new HashMap<String, Object>();
		paras.put("project_name", projectName);
		merge("project.vm", projectFile, paras);
	}

	private void createSettings() {
		File settingsFile = new File(getProject().getBaseDir(), ".settings");
		if (settingsFile.exists()) {
			log(".settings directory already exists! Skipped creating settings! ");
			return;
		} else {
			settingsFile.mkdir();
		}

		File corePrefsFile = new File(settingsFile,
				"org.eclipse.core.resources.prefs");
		File jdtPrefsFile = new File(settingsFile, "org.eclipse.jdt.core.prefs");
		File runtimePrefsFile = new File(settingsFile,
				"org.eclipse.core.runtime.prefs");

		Map<String, Object> corePrefParas = new HashMap<String, Object>();
		Map<String, Object> jdtPrefParas = new HashMap<String, Object>();
		Map<String, Object> runtimeParas = new HashMap<String, Object>();

		jdtPrefParas.put("compileversion", String.valueOf(compileversion));

		//
		merge("org.eclipse.core.resources.prefs.vm", corePrefsFile,
				corePrefParas);
		merge("org.eclipse.jdt.core.prefs.vm", jdtPrefsFile, jdtPrefParas);
		merge("org.eclipse.core.runtime.prefs.vm", runtimePrefsFile,
				runtimeParas);
	}

	private void merge(String templateName, File destFile,
			Map<String, Object> paras) {
		try {
			final String vmLoaderClassName = "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader";
			final String vmLogSysClassName = "org.apache.velocity.runtime.log.NullLogSystem";
			final String vmLoadPath = "com/xmxsuperstar/ant/task/eclipseproject/";

			VelocityEngine ve = new VelocityEngine();

			Properties p = new Properties();
			p.put("file.resource.loader.class", vmLoaderClassName);
			p.put("runtime.log.logsystem.class", vmLogSysClassName);

			ve.init(p);

			Template t = ve.getTemplate(vmLoadPath + templateName);
			VelocityContext context = new VelocityContext();

			Set<String> paraNameSet = paras.keySet();

			for (String paraName : paraNameSet) {
				context.put(paraName, paras.get(paraName));
			}

			context.put("StrUtils", new StringUtils());
			context.put("date", new Date());

			BufferedWriter writer = new BufferedWriter(new FileWriter(destFile));
			t.merge(context, writer);
			writer.flush();
			writer.close();
			this.log("Created file: " + destFile.getAbsolutePath());
		} catch (Exception e) {
			throw new BuildException(e);
		}
	}

	public static void main(String[] args) {
		EclipseProjectTask t = new EclipseProjectTask();
		t.createClassPath();
	}

}